﻿#include <QApplication>
#include <QWidget>


//在Windows平台去掉黑窗口：
#pragma comment(linker,"/subSystem:console /entry:mainCRTStartup")
//如果想让黑窗口出来，就把Windows 改成 console

#define JSON_XML 0

/*
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集，
采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写，同时也易于机器解析和生成，并有效地提升网络传输效率。

	在Qt库中，为JSON的相关操作提供了完整的类支持，包括QJsonValue，QJsonObject，QJsonArray，QJsonDocument和QJsonParseError。
		QJsonValue类表示json格式中的一个值；
		QJsonObject表示一个json对象；
		QJsonArray顾名思义表示一个json数组；
		QJsonDocument主要用来读写json文档；
		而QJsonParseError是用来表示json解析过程中出现的错误的方便类。
		
		
		
	QJsonDocument
		QJsonDocument是一个包装完整JSON文档的类，可以从UTF-8编码的基于文本的表示中读取该文档，并将其写入其中。
		JSON文档可以使用QJsonDocument::fromJson()将其基于文本的表示形式转换为QJsonDocument。toJson()将其转换回文本。解析器非常快速和有效，并将JSON转换为Qt使用的二进制表示。


	QJsonParseError
		QJsonParseError类用于报告JSON解析过程中的错误。使用唯一的成员函数`errorString()`获取错误字符串，使用成员变量`error`获取错误码，使用成员变量`offset`获取错误位置。


	QJsonObject
		JSON对象是键值对的列表，其中键是唯一的字符串，值由QJsonValue表示。
		QJsonObject可以与QVariantMap进行转换。您可以使用size()、insert()和remove()查询(key, value)对的数量，并使用标准的c++迭代器模式遍历其内容。


	QJsonArray
		JSON数组是一个值列表。可以通过从数组中插入和删除QJsonValue来操作该列表。
		QJsonArray可以与QVariantList进行转换。您可以使用size()、insert()和removeAt()条目从它中查询条目的数量，并使用标准的C++迭代器模式迭代其内容。

	
	QJsonValue
		JSON是一种存储结构化数据的格式。它有6种基本数据类型:
			bool	QJsonValue::Bool
			double	QJsonValue::Double
			string	QJsonValue::String
			array	QJsonValue::Array
			object	QJsonValue::Object
			null	QJsonValue::Null

		值可以表示上述任何一种数据类型。此外，QJsonValue有一个特殊标志来表示未定义的值。这可以用isUndefined()查询。
		值的类型可以用type()或访问器查询，如isBool()， isString()等。同样，可以使用toool()、toString()等方法将值转换为存储在其中的类型。
		值在内部是严格类型的，与QVariant相反，它不会尝试进行任何隐式类型转换。这意味着转换为未存储在值中的类型将返回默认构造的返回值。

	
	Json生成
		生成也是用上面的类，需要注意的是：生成json时会自动把key排序

*/

#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
#include <QFile>
#include <QJsonArray>

class Widget : public QWidget
{
	Q_OBJECT

public:
	Widget(QWidget* parent = nullptr)
		:QWidget(parent)
	{
		this->resize(640, 480);
	
		//parseBaseJson();

		parseJson();
	}


	void parseBaseJson()
	{
		//创建JsonError对象：
		QJsonParseError jerr;

		//创建JsonDocument对象：
		QJsonDocument jdoc = QJsonDocument::fromJson(R"({"name":"lifei"})", &jerr);
						//如果发生错误，会把错误信息传给jerr中；

		//判断是否解析成功：只要有问题，就返回true
		if (jdoc.isNull())
		{
			qWarning() << "json pase failed :>" << jerr.errorString()	//错误信息
				<< jerr.error		//错误码
				<< jerr.offset;		//错误偏移

			return;
		}


		//获取里面的数据：
		//直接获取根2对象：
		auto jobj = jdoc.object();

		//获取子对象：
		auto jvalue = jobj.value("name");
		qInfo() <<"子对象：>" << jvalue.toString();





	}

	void parseJson()
	{
		QFile file("./Json.json");
			//json文件一般都要utf-8的格式，其他格式可能打不开的
		if (!file.open(QIODevice::ReadOnly))
		{
			qWarning() << file.fileName() << "open faild :>" << file.errorString();
			return;
		}
		QJsonDocument jdoc = QJsonDocument::fromJson(file.readAll());
		if (jdoc.isNull())
		{
			qWarning() << "json parse failed";
			return;
		}


		auto jobj = jdoc.object();
		//解析普通的键值对：
		if (jobj.contains("name"))		//万无一失
		{
			qInfo() <<"name>" << jobj.value("name").toString();
		}
		if (jobj.contains("age"))
		{
			qInfo() << "age>" << jobj.value("age").toInt();
		}

		//解析对象：
		if (jobj.contains("father"))
		{
			auto sobj = jobj.value("father");
			if (sobj.isObject())
			{
				auto ssobj = sobj.toObject();
				if (ssobj.contains("name"))
				{
					qInfo() << "name>>" << ssobj.value("name").toString();
				}
				if (ssobj.contains("age"))
				{
					qInfo() << "age>>" << ssobj.value("age").toInt();
				}


			}
		}

		//解析数组：
		if (jobj.contains("girlfriend"))
		{
			auto jvalue = jobj.value("girlfriend");
			if (jvalue.isArray())			//如果是数组
			{
				auto jarray = jvalue.toArray();
				for (auto item : jarray)
				{
					//如果数据类型时string的：
					if (item.isString())
						qInfo() << item.toString();
					//如果数据类型是double的，double可以存储int类型
					else if (item.isDouble())
						qInfo() << item.toDouble();
				}

			}
		}








	}

	//生成Json:
	void genJson()
	{
		auto jdoc = QJsonDocument();

		//创建一个根对象：
		QJsonObject root;

		//添加数据
			//1.添加一个ley-value键值：
		QJsonValue nameV("一个憋");
		root.insert("name", nameV);

		root.insert("age", 18);

			//2.添加对象：
		QJsonObject sexp;
		sexp.insert("name", "李飞");
		sexp.insert("age", 55);
			//3.添加数组：
		QJsonArray children = { "liqi","liqiongyi" };
		sexp.insert("children", children);
		root.insert("sexp", sexp);

		//把跟对象设置给doc：
		jdoc.setObject(root);




		//输出json：
		qInfo() << QString::fromUtf8(jdoc.toJson(QJsonDocument::JsonFormat::Compact));



	}
};


//SJsonData:


#include "SJsonData.h"

class SJWidget : public QWidget
{
	Q_OBJECT
public:
	SJWidget(QWidget* parent = nullptr)
		:QWidget(parent)
	{
		testJsonData();
	}

	void testJsonData()
	{
		SJsonData jd;

		jd.addValue("name", "李飞");
		jd.addValue("nums",QList<double>()<<1<<2<<3<<4);

		qInfo() << jd.toJson();

		qInfo() << jd.numberArray("nums");

		jd.boolvalue("ss");
	}
};


/*
XML:
	可扩展标记语言，[标准通用标记语言](通用标言是1986年国际标准化组织出版发布的一个信息管理方面的国际标准)(Extensible Markup Language)的子集，
		简称XML，是一种定义电子文档结构和描述其内容的国际标准语言，被设计用来传输和存储数据。
	可扩展标记语言与Access]，Oracle]和SQL Server等数据库不同，数据库提供了更强有力的数据存储和分析能力，例如：数据索引、排序、查找、相关一致性等，可扩展标记语言仅仅是存储数据。
		事实上它与其他数据表现形式最大的不同是：可扩展标记语言极其简单，这是一个看上去有点琐细的优点，但正是这点使它与众不同。
	XML的简单易于在任何应用程序中读/写数据，这使XML很快成为数据交换的唯一公共语言，虽然不同的应用软件也支持其他的数据交换格式，但不久之后它们都将支持XML，
		那就意味着程序可以更容易的与[Windows]()、[Mac OS]()、[Linux]()以及其他平台下产生的信息结合，然后可以很容易加载XML数据到程序中并分析它，并以XML格式输出结果。

与HTML的区别
	(1)可扩展性方面：HTML不允许用户自行定义他们自己的标识或属性，而在XML中，用户能够根据需要自行定义新的标识及属性名，以便更好地从语义上修饰数据。
	(2)结构性方面：HTML不支持深层的结构描述，XML的文件结构嵌套可以复杂到任意程度，能表示面向对象的等级层次。
	(3)可校验性方面：HTML没有提供规范文件以支持应用软件对HTML文件进行结构校验，而XML文件可以包括一个语法描述，使应用程序可以对此文件进行结构校验。
	总之，XML使用一个简单而又灵活的标准格式，为基于Web的应用提供了一个描述数据和交换数据的有效手段。但是，XML并非是用来取代HTML的.
		HTML着重如何描述将文件显示在浏览器中，而XML，它着重描述如何将数据以结构化方式表示。


格式：
	XML文件格式是纯文本格式，在许多方面类似于HTML，XML由XML元素组成，每个XML元素包括一个开始标记<>，一个结束标记</>以及两个标记之间的内容，
	例如，可以将XML元素标记为价格、订单编号或名称。标记是对文档存储格式和逻辑结构的描述。
	在形式上，标记中可能包括注释、引用、字符数据段、起始标记、结束标记、空元素、文档类型声明( DTD)和序言。

		具体规则如下：
			1、必须有声明语句。
				XML声明是XML文档的第一句，其格式如下：
				<?xml version="1.0" encoding="utf-8"?>
			2、注意大小写
				在XML文档中，大小写是有区别的。“A”和“a”是不同的标记。注意在写元素时，前后标记的大小写要保持一致。最好养成一种习惯，或者全部大写，或者全部小写，或者大写第一个字母，
				这样可以减少因为大小写不匹配而产生的文档错误。
			3、XML文档有且只有一个根元素
				良好格式的XML文档必须有一个根元素，就是紧接着声明后面建立的第一个元素，其他元素都是这个根元素的子元素，根元素完全包括文档中其他所有的元素。
				根元素的起始标记要放在所有其他元素的起始标记之前；根元素的结束标记要放在所有其他元素的结束标记之后。
			4、属性值使用引号
				在HTML代码里面，属性值可以加引号，也可以不加。但是XML规定，所有属性值必须加引号（可以是单引号，也可以是双引号，建议使用双引号），否则将被视为错误。
			5、所有的标记必须有相应的结束标记
				在HTML中，标记可以不成对出现，而在XML中，所有标记必须成对出现，有一个开始标记，就必须有一个结束标记，否则将被视为错误。
			6、所有的空标记也必须被关闭
				空标记是指标记对之间没有内容的标记， 由于空标记不含任何内容，所以空标记不需要开始标签和结束标签，空标记“<”标识开始，以“/>”标识结束，如：比如<door color="red"/>
		
		
xml解析：
	Qt 提供了两种读取 XML 文档的方法：
		QXmlStreamReader：				快
			一种快速的基于流的方式访问良格式 XML 文档，特别适合于实现一次解析器（所谓“一次解析器”，可以理解成我们只需读取文档一次，然后像一个遍历器从头到尾一次性处理 XML 文档，
			期间不会有反复的情况，也就是不会读完第一个标签，然后读第二个，读完第二个又返回去读第一个，这是不允许的）；
		DOM（Document Object Model）：	占内存
			将整个 XML 文档读入内存，构建成一个树结构，允许程序在树结构上向前向后移动导航，这是与另外两种方式最大的区别，
			也就是允许实现多次解析器（对应于前面所说的一次解析器）。DOM 方式带来的问题是需要一次性将整个 XML 文档读入内存，因此会占用很大内存；
		
*/

#include <QDomDocument>
#include <QDomNode>


class XmlWidget :public QWidget
{
public:
	XmlWidget(QWidget* parent = nullptr)
		:QWidget(parent)
	{
		//parseBaseXml();

		//parseXml();

		genXml();
	}

	void parseBaseXml()
	{
		QFile file("xml.xml");
		if (!file.open(QIODevice::ReadOnly))
		{
			qWarning() << file.fileName() << "xml open failed>" << file.errorString();
			return;
		}

		//错误信息数据：
		QString errmsg;
		int errLine;
		int errCol;


		//创建dom文档：
		QDomDocument dom;

		//设置内容：
		bool ok = dom.setContent(&file,&errmsg,&errLine,&errCol);
		if (!ok)
		{
			qWarning() << "xml parse failed >" << QString("row:%1,col:%2").arg(errLine).arg(errCol);
			return;
		}


		//获取版本和编码信息：（生明语句的信息）
		qInfo() << dom.firstChild().nodeName()<<dom.firstChild().nodeValue();	//"xml" "version='1.0' encoding='utf-8'"


		//获取指定名称为指定值的所有标签：
		auto list = dom.elementsByTagName("name");
		for (int i = 0; i < list.size(); i++)
		{
			auto node = list.at(i);
			qInfo() << node.nodeName() << node.toElement().text();
		}


	}

	void parseXml()
	{
		QFile file("xml.xml");
		if (!file.open(QIODevice::ReadOnly))
		{
			qWarning() << file.fileName() << "xml open failed>" << file.errorString();
			return;
		}

		//错误信息数据：
		QString errmsg;
		int errLine;
		int errCol;


		//创建dom文档：
		QDomDocument dom;

		//设置内容：
		bool ok = dom.setContent(&file, &errmsg, &errLine, &errCol);
		if (!ok)
		{
			qWarning() << "xml parse failed >" << QString("row:%1,col:%2").arg(errLine).arg(errCol);
			return;
		}


		//拿到根节点：
		auto emt = dom.documentElement();
		qInfo() << emt.nodeName();		//"root"
		if (emt.tagName() != "root")
		{
			qWarning() << "哇哦，这个xml貌似不是我的理想型唉";
			return;
		}
		
		//遍历：
#if 0
		//获取到根节点下的第一个元素节点：
		auto enode = emt.firstChild().toElement();
		//开始遍历：
		while (!enode.isNull())
		{
			qInfo() << enode.tagName();
			//获取同级的下一个节点:
			enode = enode.nextSibling().toElement();
			//所以就是一般情况下，只能一个级别的一个级别的遍历？
			//那我想要获取所有的级别的呢		这个就是不行的了；

		}
#else
		parseNode(emt);


#endif

	}

	void parseNode(const QDomElement& e)
	{
		auto enode = e.firstChild().toElement();

		//开始遍历：
		while (!enode.isNull())
		{
			qInfo() << enode.tagName()<<enode.text();

			parseNode(enode);

			//获取同级的下一个节点：
			enode = enode.nextSibling().toElement();
		}

		/*
			"root"
			"name" "李飞"
			"age" "21"
			"father" "李琪66141516"
			"name" "李琪"
			"age" "66"
			"children" "141516"
			"age" "14"
			"age" "15"
			"age" "16"
			"girlfriend" "lizuozhangzhu666"
			"name" "li"
			"name" "zuo"
			"name" "zhang"
			"name" "zhu"
			"name" "666"
		
		*/
	}

	void genXml()
	{
		QDomDocument doc;

		auto e = doc.createElement("root");
		e.appendChild(doc.createTextNode("李飞"));

		auto e1 = doc.createElement("hello");
		e1.appendChild(doc.createTextNode("李淇"));

		e.appendChild(e1);

		doc.appendChild(e);

		qInfo() << doc.toString();


	}
};

int main(int argc, char* argv[])
{
	QApplication a(argc, argv);
#if JSON_XML

	#if 0
		Widget w;
		w.show();
	#else
		SJWidget w;
		w.show();
	#endif

#else

	XmlWidget w;
	w.show();

#endif

	return a.exec();
}



#include "main.moc"
//如果点击运行或者调试，没有出现错误，但是就是生成失败，有可以就是缺少这个；

